import { Tabs } from 'nextra/components'
import { Callout, Sandpack } from '@/components'

# ErrorBoundary

이 컴포넌트는 children에 에러가 발생하면 제어할 수 있습니다.

## 비교

`@suspensive/react`의 `<ErrorBoundary/>`는 React의 클래스 기반 에러 경계와 인기 있는 `react-error-boundary` 및 `@sentry/react`와 같은 에러 경계 라이브러리에 대한 선언적이고 기능이 풍부한 대안을 제공합니다.

| 기능                                 | @suspensive/react | react-error-boundary   | @sentry/react          | React 클래스 컴포넌트  |
| ------------------------------------ | ----------------- | ---------------------- | ---------------------- | ---------------------- |
| 기본 에러 캐칭                       | ✅                | ✅                     | ✅                     | ✅                     |
| 에러 및 리셋 기능이 있는 Fallback UI | ✅                | ✅                     | ✅                     | ⚠️ (수동)              |
| resetKeys로 리셋                     | ✅                | ✅                     | ❌                     | ⚠️ (수동)              |
| onReset 콜백                         | ✅                | ✅                     | ✅                     | ⚠️ (수동)              |
| onError 콜백                         | ✅                | ✅                     | ✅                     | ✅ (componentDidCatch) |
| 조건부 에러 캐칭 (shouldCatch)       | ✅                | ❌                     | ❌                     | ⚠️ (수동)              |
| Fallback 에러 처리                   | ✅ (부모로 전달)  | ❌ (재귀적)            | ❌ (재귀적)            | ⚠️ (수동)              |
| useErrorBoundary 훅                  | ✅                | ✅                     | ❌                     | ❌                     |
| useErrorBoundaryFallbackProps 훅     | ✅                | ❌                     | ❌                     | ❌                     |
| ErrorBoundaryGroup                   | ✅                | ❌                     | ❌                     | ❌                     |
| HOC 지원                             | ✅ (with)         | ✅ (withErrorBoundary) | ✅ (withErrorBoundary) | ❌                     |
| TypeScript 에러 타입 추론            | ✅ (고급)         | ✅ (기본)              | ✅ (기본)              | ⚠️ (수동)              |
| 선언적 API                           | ✅                | ✅                     | ✅                     | ❌                     |
| 자동 에러 보고                       | ❌                | ❌                     | ✅ (Sentry로)          | ❌                     |

<Tabs items={['@suspensive/react', 'react-error-boundary', '@sentry/react', 'React 클래스']}>
  <Tabs.Tab>

```tsx
import { ErrorBoundary } from '@suspensive/react'

const SuspensiveExample = () => (
  <ErrorBoundary
    fallback={({ error, reset }) => (
      <div>
        <button onClick={reset}>리셋</button>
        {error.message}
      </div>
    )}
  >
    <YourComponent />
  </ErrorBoundary>
)
```

  </Tabs.Tab>
  <Tabs.Tab>

```tsx
import { ErrorBoundary } from 'react-error-boundary'

const ReactErrorBoundaryExample = () => (
  <ErrorBoundary
    fallbackRender={({ error, resetErrorBoundary }) => (
      <div>
        <button onClick={resetErrorBoundary}>리셋</button>
        {error.message}
      </div>
    )}
  >
    <YourComponent />
  </ErrorBoundary>
)
```

  </Tabs.Tab>
  <Tabs.Tab>

```tsx
import { ErrorBoundary } from '@sentry/react'

const SentryExample = () => (
  <ErrorBoundary
    fallback={({ error, resetError }) => (
      <div>
        <button onClick={resetError}>리셋</button>
        {error.message}
      </div>
    )}
  >
    <YourComponent />
  </ErrorBoundary>
)
```

  </Tabs.Tab>
  <Tabs.Tab>

```tsx
import { Component, ReactNode } from 'react'

class ErrorBoundary extends Component<
  { children: ReactNode; fallback: (error: Error) => ReactNode },
  { hasError: boolean; error: Error | null }
> {
  constructor(props) {
    super(props)
    this.state = { hasError: false, error: null }
  }

  static getDerivedStateFromError(error: Error) {
    return { hasError: true, error }
  }

  componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    console.error('에러 캐치:', error, errorInfo)
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback(this.state.error!)
    }
    return this.props.children
  }
}

// 사용법
const ReactClassErrorBoundaryExample = () => (
  <ErrorBoundary
    fallback={(error) => (
      <div>
        <button onClick={() => window.location.reload()}>리셋</button>
        {error.message}
      </div>
    )}
  >
    <YourComponent />
  </ErrorBoundary>
)
```

  </Tabs.Tab>
</Tabs>

### @suspensive/react의 주요 장점

1. **shouldCatch를 통한 고급 에러 필터링**: 다른 솔루션과 달리, `@suspensive/react`는 boolean, ErrorConstructor 또는 콜백 매처를 사용하여 특정 에러를 조건부로 캐치할 수 있습니다. 이를 통해 부모와 자식 ErrorBoundary가 서로 다른 에러 타입을 처리할 수 있는 정교한 에러 처리 전략을 구현할 수 있습니다.

2. **적절한 Fallback 에러 처리**: `react-error-boundary`와 달리, fallback 컴포넌트에서 발생한 에러는 같은 경계에서 재귀적으로 캐치되는 대신 부모 ErrorBoundary로 전달됩니다. 이를 통해 무한 fallback 루프를 방지하고 더 예측 가능한 에러 처리 동작을 제공합니다. [자세히 알아보기](/docs/react/migration/migrate-to-v3#now-thrown-error-in-errorboundary-fallback-will-be-passed-to-parent-1409)

3. **useErrorBoundaryFallbackProps**: fallback 컴포넌트에서 prop drilling을 제거하고 훅을 통해 `error`와 `reset`에 직접 접근할 수 있어, 깊게 중첩된 fallback UI를 훨씬 깔끔하게 만들 수 있습니다.

4. **ErrorBoundaryGroup**: 여러 ErrorBoundary를 함께 관리하고 리셋할 수 있으며, 조정된 리셋 동작이 필요한 여러 에러 경계가 있는 복잡한 UI에 완벽합니다.

5. **더 나은 TypeScript 지원**: shouldCatch 구성을 기반으로 에러 타입에 대한 고급 타입 추론을 제공하여 더 나은 자동 완성과 타입 안정성을 제공합니다.

6. **클래스 컴포넌트 불필요**: 네이티브 React 에러 경계와 달리, 클래스 컴포넌트를 작성하지 않고도 완전히 선언적인 함수 컴포넌트 기반 접근 방식을 사용할 수 있습니다.

### 마이그레이션 가이드

<Tabs items={['react-error-boundary에서', '@sentry/react에서', 'React 클래스 컴포넌트에서']}>
<Tabs.Tab>

react-error-boundary를 사용하고 있다면, `@suspensive/react`로 마이그레이션하는 것은 간단합니다:

```tsx
// react-error-boundary
import { ErrorBoundary } from 'react-error-boundary'

const ReactErrorBoundaryExample = () => (
  <ErrorBoundary
    fallbackRender={({ error, resetErrorBoundary }) => (
      <div>
        <button onClick={resetErrorBoundary}>리셋</button>
        {error.message}
      </div>
    )}
    onReset={() => console.log('reset')}
  >
    <YourComponent />
  </ErrorBoundary>
)

// @suspensive/react - 동일한 기능
import { ErrorBoundary } from '@suspensive/react'

const SuspensiveExample = () => (
  <ErrorBoundary
    fallback={({ error, reset }) => (
      <div>
        <button onClick={reset}>리셋</button>
        {error.message}
      </div>
    )}
    onReset={() => console.log('reset')}
  >
    <YourComponent />
  </ErrorBoundary>
)
```

주요 API 차이점:

- `fallback`, `fallbackRender`, `FallbackComponent` → `fallback`
- `resetErrorBoundary` → `reset` (fallback props에서)

</Tabs.Tab>
<Tabs.Tab>

`@sentry/react`를 사용하고 있다면, `@suspensive/react`로 마이그레이션하는 것은 간단합니다:

```tsx
// @sentry/react
import * as Sentry from '@sentry/react'

const SentryExample = () => (
  <Sentry.ErrorBoundary
    fallback={({ error, resetError }) => (
      <div>
        <button onClick={resetError}>리셋</button>
        {error.message}
      </div>
    )}
    onError={(error, componentStack, eventId) => {
      // 에러가 자동으로 Sentry로 전송됨
      console.log('에러 캐치:', eventId)
    }}
  >
    <YourComponent />
  </Sentry.ErrorBoundary>
)

// @suspensive/react - 수동 Sentry 통합과 함께
import { ErrorBoundary } from '@suspensive/react'
import * as Sentry from '@sentry/react'

const SuspensiveExample = () => (
  <ErrorBoundary
    fallback={({ error, reset }) => (
      <div>
        <button onClick={reset}>리셋</button>
        {error.message}
      </div>
    )}
    onError={(error, errorInfo) => {
      const eventId = Sentry.captureReactException(error, errorInfo)
      console.log('에러 캐치:', eventId)
    }}
  >
    <YourComponent />
  </ErrorBoundary>
)
```

주요 차이점:

- `@sentry/react`는 자동으로 Sentry에 에러를 보고합니다
- `@suspensive/react`는 `shouldCatch`를 통해 더 유연한 에러 처리를 제공합니다
- `resetError` → `reset` (fallback props에서)
- 수동 Sentry 통합은 무엇을 보고할지 더 많은 제어를 제공합니다

</Tabs.Tab>
<Tabs.Tab>

React 클래스 컴포넌트를 에러 경계로 사용하고 있다면, `@suspensive/react`는 훨씬 간단한 선언적 API를 제공합니다:

```tsx
// React 클래스 컴포넌트
class ErrorBoundary extends Component {
  state = { hasError: false, error: null }

  static getDerivedStateFromError(error) {
    return { hasError: true, error }
  }

  componentDidCatch(error, errorInfo) {
    logErrorToService(error, errorInfo)
  }

  reset = () => {
    this.setState({ hasError: false, error: null })
  }

  render() {
    if (this.state.hasError) {
      return <FallbackComponent error={this.state.error} reset={this.reset} />
    }
    return this.props.children
  }
}

// @suspensive/react - 훨씬 간단합니다!
import { ErrorBoundary } from '@suspensive/react'

const SuspensiveExample = () => (
  <ErrorBoundary
    fallback={({ error, reset }) => (
      <FallbackComponent error={error} reset={reset} />
    )}
    onError={(error, errorInfo) => logErrorToService(error, errorInfo)}
  >
    <YourComponent />
  </ErrorBoundary>
)
```

</Tabs.Tab>
</Tabs>

---

### props.fallback

`<ErrorBoundary/>`의 children에 error가 발생하면 error는 잡히고 fallback이 렌더링됩니다.

```tsx /fallback/
import { ErrorBoundary } from '@suspensive/react'
import { useState, useEffect } from 'react'

const Example = () => (
  <ErrorBoundary
    fallback={(props) => (
      <>
        <button onClick={props.reset}>Try again</button>
        {props.error.message}
      </>
    )}
  >
    <ErrorAfter2s />
  </ErrorBoundary>
)
```

<Sandpack>

```tsx Example.tsx active
import { ErrorBoundary } from '@suspensive/react'
import { useState, useEffect } from 'react'
import { ErrorAfter2s } from './ErrorAfter2s'

export const Example = () => {
  return (
    <ErrorBoundary
      fallback={(props) => (
        <>
          <button onClick={props.reset}>Try again</button>
          {props.error.message}
        </>
      )}
    >
      <ErrorAfter2s />
    </ErrorBoundary>
  )
}
```

```tsx ErrorAfter2s.tsx
import { useState, useEffect } from 'react'

export const ErrorAfter2s = () => {
  const [asyncState, setAsyncState] = useState<
    { isError: true; error: Error } | { isError: false; error: null }
  >({
    isError: false,
    error: null,
  })

  useEffect(() => {
    setTimeout(() => {
      setAsyncState({ isError: true, error: new Error('error made by Error') })
    }, 2000)
  }, [])

  if (asyncState.isError) {
    throw asyncState.error
  }

  return <>No error</>
}
```

</Sandpack>

<Callout type='info'>

`<ErrorBoundary/>`의 fallback으로 전달할 컴포넌트 정의하기

#### ErrorBoundaryFallbackProps

`<ErrorBoundary/>`의 fallback으로 컴포넌트를 전달하고 싶다면 `ErrorBoundaryFallbackProps` 타입을 활용해 쉽게 컴포넌트를 선언할 수 있습니다.

```tsx /ErrorBoundaryFallbackProps/
import type { ErrorBoundaryFallbackProps } from '@suspensive/react'

const ErrorBoundaryFallback = ({
  reset,
  error,
}: ErrorBoundaryFallbackProps) => (
  <>
    <button onClick={reset}>reset</button>
    {error.message}
  </>
)

const Example = () => (
  <ErrorBoundary fallback={ErrorBoundaryFallback}>
    <ErrorAfter2s />
  </ErrorBoundary>
)
```

</Callout>

<Callout>

`<ErrorBoundary/>` fallback props을 prop drilling 없이 사용하기

#### useErrorBoundaryFallbackProps

`error` 객체와 `reset` 메소드를 사용하려는 컴포넌트가 중첩되면 prop drilling을 피할 수 없습니다.
이 때, `useErrorBoundaryFallbackProps`을 통해, prop drilling 없이 `reset` 메소드와 `error` 객체에 접근할 수 있습니다.

```tsx /useErrorBoundaryFallbackProps/
import { ErrorBoundary, useErrorBoundaryFallbackProps } from '@suspensive/react'

const Nested = () => {
  const { reset, error } = useErrorBoundaryFallbackProps()

  return (
    <>
      <button onClick={reset}>Try again</button>
      {error.message}
    </>
  )
}

// 여기서 fallbackProp 을 전달할 필요가 없어집니다!
const ErrorBoundaryFallback = () => <Nested />

const Example = () => (
  <ErrorBoundary fallback={ErrorBoundaryFallback}>
    <ErrorAfter2s />
  </ErrorBoundary>
)
```

</Callout>

### props.resetKeys

`<ErrorBoundary/>`의 fallback 외부에 있는 컴포넌트가 `<ErrorBoundary/>`를 reset하려면 resetKeys배열에 resetKey를 할당하면 됩니다. resetKeys는 배열의 하나 이상의 요소가 변경된 경우에만 작동합니다. useEffect의 종속성 배열이 작동하는 방식과 같이 resetKeys로 매 렌더링마다 새 배열을 주입하는 것을 걱정할 필요도 없습니다.

```tsx /resetKeys/
import { ErrorBoundary } from '@suspensive/react'
import { useState, useEffect } from 'react'

const Example = () => {
  const [resetKey, setResetKey] = useState(0)

  return (
    <>
      <button onClick={() => setResetKey((prev) => prev + 1)}>Try again</button>
      <ErrorBoundary
        resetKeys={[resetKey]}
        fallback={(props) => <>{props.error.message}</>}
      >
        <ErrorAfter2s />
      </ErrorBoundary>
    </>
  )
}
```

<Sandpack>

```tsx Example.tsx active
import { ErrorBoundary } from '@suspensive/react'
import { useState, useEffect } from 'react'
import { ErrorAfter2s } from './ErrorAfter2s'

export const Example = () => {
  const [resetKey, setResetKey] = useState(0)

  return (
    <>
      <button onClick={() => setResetKey((prev) => prev + 1)}>Try again</button>
      <ErrorBoundary
        resetKeys={[resetKey]}
        fallback={(props) => <>{props.error.message}</>}
      >
        <ErrorAfter2s />
      </ErrorBoundary>
    </>
  )
}
```

```tsx /ErrorAfter2s.tsx
import { useState, useEffect } from 'react'

export const ErrorAfter2s = () => {
  const [asyncState, setAsyncState] = useState<
    { isError: true; error: Error } | { isError: false; error: null }
  >({
    isError: false,
    error: null,
  })

  useEffect(() => {
    setTimeout(() => {
      setAsyncState({ isError: true, error: new Error('error made by Error') })
    }, 2000)
  }, [])

  if (asyncState.isError) {
    throw asyncState.error
  }

  return <>No error</>
}
```

</Sandpack>

### props.onReset

`<ErrorBoundary/>`가 reset할 때 먼저 호출되는 callback입니다. @tanstack/react-query와는 아래와 같이 사용할 수 있습니다.

```tsx /onReset/
import { ErrorBoundary } from '@suspensive/react'
import { QueryErrorResetBoundary } from '@tanstack/react-query'

const Example = () => (
  <QueryErrorResetBoundary>
    {({ reset }) => (
      <ErrorBoundary
        onReset={reset}
        fallback={(props) => (
          <>
            <button onClick={props.reset}>Try again</button>
            {props.error.message}
          </>
        )}
      >
        <Page />
      </ErrorBoundary>
    )}
  </QueryErrorResetBoundary>
)
```

<Sandpack>

```tsx Example.tsx active
import { ErrorBoundary } from '@suspensive/react'
import { QueryErrorResetBoundary } from '@tanstack/react-query'
import { Page } from './Page'

export const Example = () => (
  <QueryErrorResetBoundary>
    {({ reset }) => (
      <ErrorBoundary
        onReset={reset}
        fallback={(props) => (
          <>
            <button onClick={props.reset}>Try again</button>
            {props.error.message}
          </>
        )}
      >
        <Page />
      </ErrorBoundary>
    )}
  </QueryErrorResetBoundary>
)
```

```tsx /Page.tsx
import { useQuery } from '@tanstack/react-query'
import { fetchSomething } from './fetchSomething'

export const Page = () => {
  const { data, isLoading, error } = useQuery({
    queryKey: ['key'],
    queryFn: fetchSomething,
    useErrorBoundary: true,
    retry: false,
  })

  if (isLoading) return <>Loading...</>

  return <>{data}</>
}
```

```tsx /fetchSomething.ts
export const fetchSomething = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject({ status: 401, message: 'unauthorized' })
    }, 2000)
  })
}
```

</Sandpack>

### props.onError

`<ErrorBoundary/>`가 error를 잡을 때 호출되는 callback입니다.

```tsx /onError/
import { ErrorBoundary } from '@suspensive/react'

const logError = (error: Error, info: ErrorInfo) => {
  // ...
}

const Example = (
  <ErrorBoundary fallback={ErrorBoundaryFallback} onError={logError}>
    <ErrorAfter2s />
  </ErrorBoundary>
)
```

<Sandpack previewOptions={{ showConsoleButton: true, showConsole: true }}>

```tsx Example.tsx active
import { ErrorBoundary } from '@suspensive/react'
import { ErrorAfter2s } from './ErrorAfter2s'

const logError = (error: Error, info: ErrorInfo) => {
  console.log(error, info)
}

export const Example = () => {
  return (
    <ErrorBoundary
      fallback={(props) => (
        <>
          <button onClick={props.reset}>Try again</button>
          {props.error.message}
        </>
      )}
      onError={logError}
    >
      <ErrorAfter2s />
    </ErrorBoundary>
  )
}
```

```tsx /ErrorAfter2s.tsx
import { useState, useEffect } from 'react'

export const ErrorAfter2s = () => {
  const [asyncState, setAsyncState] = useState<
    { isError: true; error: Error } | { isError: false; error: null }
  >({
    isError: false,
    error: null,
  })

  useEffect(() => {
    setTimeout(() => {
      setAsyncState({ isError: true, error: new Error('error made by Error') })
    }, 2000)
  }, [])

  if (asyncState.isError) {
    throw asyncState.error
  }

  return <>No error</>
}
```

</Sandpack>

### props.shouldCatch

shouldCatch는 조건에 따라 `<ErrorBoundary/>`가 에러를 잡을지 결정합니다.

Boolean, ErrorConstructor, Callback의 3가지 기준을 받으며 기본값은 `true`입니다.

<Tabs items={['ErrorConstructor', 'Callback', 'Boolean']}>
<Tabs.Tab>

```tsx /shouldCatch/
import { ErrorBoundary } from '@suspensive/react'
import { useState, useEffect, createElement } from 'react'

export const Example = () => {
  return (
    <ErrorBoundary
      fallback={({ error }) => (
        <>Parent ErrorBoundary fallback: {error.message}</>
      )}
    >
      <ErrorBoundary
        shouldCatch={CustomError}
        fallback={({ error }) => (
          <>Child ErrorBoundary fallback: {error.message}</>
        )}
      >
        <CustomErrorAfter2s />
      </ErrorBoundary>
    </ErrorBoundary>
  )
}
```

</Tabs.Tab>
<Tabs.Tab>

```tsx /shouldCatch/
import { ErrorBoundary } from '@suspensive/react'
import { useState, useEffect, createElement } from 'react'

export const Example = () => {
  return (
    <ErrorBoundary
      fallback={({ error }) => (
        <>Parent ErrorBoundary fallback: {error.message}</>
      )}
    >
      <ErrorBoundary
        shouldCatch={(error) => error instanceof CustomError}
        fallback={({ error }) => (
          <>Child ErrorBoundary fallback: {error.message}</>
        )}
      >
        <CustomErrorAfter2s />
      </ErrorBoundary>
    </ErrorBoundary>
  )
}
```

</Tabs.Tab>
<Tabs.Tab>

```tsx /shouldCatch/
import { ErrorBoundary } from '@suspensive/react'
import { useState, useEffect, createElement } from 'react'

export const Example = () => {
  return (
    <ErrorBoundary
      fallback={({ error }) => (
        <>Parent ErrorBoundary fallback: {error.message}</>
      )}
    >
      <ErrorBoundary
        shouldCatch={false}
        fallback={({ error }) => (
          <>Child ErrorBoundary fallback: {error.message}</>
        )}
      >
        <CustomErrorAfter2s />
      </ErrorBoundary>
    </ErrorBoundary>
  )
}
```

</Tabs.Tab>
</Tabs>

배열을 통해 여러 조건을 적용할 수도 있습니다.

```tsx /shouldCatch/
import { ErrorBoundary } from '@suspensive/react'
import { useState, useEffect, createElement } from 'react'

const Example = () => {
  return (
    <ErrorBoundary
      fallback={({ error }) => (
        <>Parent ErrorBoundary fallback: {error.message}</>
      )}
    >
      <ErrorBoundary
        shouldCatch={[
          false,
          CustomError,
          (error) => error instanceof CustomError,
        ]}
        fallback={({ error }) => (
          <>Child ErrorBoundary fallback: {error.message}</>
        )}
      >
        <CustomErrorAfter2s />
      </ErrorBoundary>
    </ErrorBoundary>
  )
}
```

<Sandpack>

```tsx Example.tsx active
import { ErrorBoundary } from '@suspensive/react'
import { useState, useEffect, createElement } from 'react'

export const Example = () => {
  return (
    <ErrorBoundary
      fallback={({ error }) => (
        <>Parent ErrorBoundary fallback: {error.message}</>
      )}
    >
      <ErrorBoundary
        shouldCatch={CustomError}
        fallback={({ error }) => (
          <>Child ErrorBoundary fallback: {error.message}</>
        )}
      >
        <CustomErrorAfter2s />
      </ErrorBoundary>
    </ErrorBoundary>
  )
}

export class CustomError extends Error {
  constructor(...args: ConstructorParameters<ErrorConstructor>) {
    super(...args)
    console.error(...args)
  }
}

export const CustomErrorAfter2s = () => {
  const [asyncState, setAsyncState] = useState<
    { isError: true; error: CustomError } | { isError: false; error: null }
  >({
    isError: false,
    error: null,
  })

  useEffect(() => {
    setTimeout(() => {
      setAsyncState({
        isError: true,
        error: () => new CustomError('error made by CustomError'),
      })
    }, 2000)
  }, [])

  if (asyncState.isError) {
    throw asyncState.error()
  }

  return <>No error</>
}
```

</Sandpack>

<br />

<Sandpack>

```tsx Example.tsx active
import { ErrorBoundary } from '@suspensive/react'
import { useState, useEffect, createElement } from 'react'

export const Example = () => {
  return (
    <ErrorBoundary
      fallback={({ error }) => (
        <>Parent ErrorBoundary fallback: {error.message}</>
      )}
    >
      <ErrorBoundary
        shouldCatch={CustomError}
        fallback={({ error }) => (
          <>Child ErrorBoundary fallback: {error.message}</>
        )}
      >
        <ErrorAfter2s />
      </ErrorBoundary>
    </ErrorBoundary>
  )
}

export class CustomError extends Error {
  constructor(...args: ConstructorParameters<ErrorConstructor>) {
    super(...args)
    console.error(...args)
  }
}

export const ErrorAfter2s = () => {
  const [asyncState, setAsyncState] = useState<
    { isError: true; error: Error } | { isError: false; error: null }
  >({
    isError: false,
    error: null,
  })

  useEffect(() => {
    setTimeout(() => {
      setAsyncState({ isError: true, error: new Error('error made by Error') })
    }, 2000)
  }, [])

  if (asyncState.isError) {
    throw asyncState.error
  }

  return <>No error</>
}
```

</Sandpack>

## ErrorBoundary.with

`ErrorBoundary.with`는 `<ErrorBoundary/>`의 props를 설정할 수 있는 HOC입니다.
`ErrorBoundary.with`를 사용하면 컴포넌트를 쉽게 래핑할 수 있습니다.

```tsx /ErrorBoundary.with/
import { ErrorBoundary, useErrorBoundary } from '@suspensive/react'

const Example = ErrorBoundary.with({ fallback: ErrorBoundaryFallback }, () => {
  const errorBoundary = useErrorBoundary()

  return <>...</>
})
```

## useErrorBoundary

### useErrorBoundary().setError

`<ErrorBoundary/>`의 children에서 useErrorBoundary().setError을 사용해 throw 없이도 `<ErrorBoundary/>`에서 Error를 알도록 할 수 있습니다.

```tsx /useErrorBoundary/
import { ErrorBoundary, useErrorBoundary } from '@suspensive/react'
import { useEffect } from 'react'

const Example = () => (
  <ErrorBoundary fallback={ErrorBoundaryFallback}>
    <SetErrorAfterFetch />
  </ErrorBoundary>
)

const SetErrorAfterFetch = () => {
  const errorBoundary = useErrorBoundary()

  useEffect(() => {
    fetchSomething().then(
      (response) => {},
      (error) => errorBoundary.setError(error) // instead of throw inside
    )
  }, [])

  return <>No error</>
}
```

<Sandpack>

```tsx Example.tsx active
import { ErrorBoundary, useErrorBoundary } from '@suspensive/react'
import { useEffect } from 'react'
import { ErrorBoundaryFallback } from './ErrorBoundaryFallback'
import { fetchSomething } from './fetchSomething'

export const Example = () => (
  <ErrorBoundary fallback={ErrorBoundaryFallback}>
    <SetErrorAfterFetch />
  </ErrorBoundary>
)

const SetErrorAfterFetch = () => {
  const errorBoundary = useErrorBoundary()

  useEffect(() => {
    fetchSomething().then(
      (response) => {},
      (error) => errorBoundary.setError(error) // instead of throw inside
    )
  }, [])

  return <>No error</>
}
```

```tsx /ErrorBoundaryFallback.tsx
import type { ErrorBoundaryFallbackProps } from '@suspensive/react'

export const ErrorBoundaryFallback = (props: ErrorBoundaryFallbackProps) => (
  <>
    <button onClick={props.reset}>Try again</button>
    {props.error.message}
  </>
)
```

```tsx /fetchSomething.ts
export const fetchSomething = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject({ status: 401, message: 'unauthorized' })
    }, 2000)
  })
}
```

</Sandpack>

<Callout>

다수의 `<ErrorBoundary/>`를 제어하기

`<ErrorBoundary/>`은 `<ErrorBoundaryGroup/>`과 사용하면 더 강력하게 사용할 수 있습니다. `<ErrorBoundaryGroup/>`로 다수의 `<ErrorBoundary/>`를 제어하세요.  
자세한 내용은 [`<ErrorBoundaryGroup/>`페이지](/docs/react/ErrorBoundaryGroup)에서 소개합니다.

</Callout>
